import os

import cv2
import numpy as np
from matplotlib import pyplot as plt


class ImageHandler:
    LOSSLESS_FORMATS = (".bmp", ".png", ".tif")
    WHITE = (255, 255, 255)

    def __init__(self, path):
        if not path.endswith(self.LOSSLESS_FORMATS):
            raise ValueError(f"Only lossless images are intended {self.LOSSLESS_FORMATS}")

        self.path = path
        self.name = os.path.basename(self.path)
        self.image = cv2.imread(self.path, cv2.IMREAD_COLOR)

    def read(self):
        self.image = cv2.imread(self.path, cv2.IMREAD_COLOR)
        cv2.imshow(self.name, self.image)

        for i, color in enumerate(("b", "g", "r")):
            histogram = cv2.calcHist([self.image], [i], None, [256], [0, 256])
            plt.plot(histogram, color=color)
        plt.title(self.name)
        plt.show()

        cv2.setMouseCallback(self.name, self.mouse_callback)

        while cv2.getWindowProperty(self.name, cv2.WND_PROP_VISIBLE) >= 1:
            key = cv2.waitKey(1)
            if key & 0xFF == 27:
                break

        cv2.destroyAllWindows()

    def mouse_callback(self, event, x, y, _, __):
        if event == cv2.EVENT_MOUSEMOVE:
            image = cv2.rectangle(self.image.copy(), (x - 7, y - 7), (x + 6, y + 6), self.WHITE, 1)
            cv2.imshow(self.name, image)
        elif event == cv2.EVENT_LBUTTONDOWN:
            print(f"Position: {x}, {y}")

            b, g, r = self.image[y, x].astype(np.uint32)
            print(f"RGB components: {r}, {g}, {b}")
            print(f"Intensity: {(r + g + b) / 3.:.2f}")

            median = 0
            for i in range(-7, 6, 1):
                for j in range(-7, 6, 1):
                    b, g, r = self.image[y + i, x + j].astype(np.uint32)
                    median += (r + g + b) / 3.
            median /= (13 ** 2)
            print(f"Average intensity: {median:.2f}")

            standard = 0
            for i in range(-7, 6, 1):
                for j in range(-7, 6, 1):
                    b, g, r = self.image[y + i, x + j].astype(np.uint32)
                    standard += ((r + g + b) / 3. - median) ** 2
            standard /= (13 ** 2)
            standard = np.sqrt(standard)
            print(f"Standard deviation: {standard:.2f}\n")


if __name__ == "__main__":
    ImageHandler("../materials/pesec.bmp").read()
